Pudgy Penguins

Etherscan Site

PudgyPenguins.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "./ERC721Pausable.sol";
contract PudgyPenguins is ERC721Enumerable, Ownable, ERC721Burnable, ERC721Pausable {
    using SafeMath for uint256;
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdTracker;

    uint256 public constant MAX_ELEMENTS = 8888;
    uint256 public constant PRICE = 3 * 10**16;
    uint256 public constant MAX_BY_MINT = 20;
    uint256 public constant reveal_timestamp = 1627588800; // Thu Jul 29 2021 20:00:00 GMT+0000
    address public constant creatorAddress = 0x6F84Fa72Ca4554E0eEFcB9032e5A4F1FB41b726C;
    address public constant devAddress = 0xcBCc84766F2950CF867f42D766c43fB2D2Ba3256;
    string public baseTokenURI;

    event CreatePenguin(uint256 indexed id);
    constructor(string memory baseURI) ERC721("PudgyPenguins", "PPG") {
        setBaseURI(baseURI);
        pause(true);
    }

    modifier saleIsOpen {
        require(_totalSupply() <= MAX_ELEMENTS, "Sale end");
        if (_msgSender() != owner()) {
            require(!paused(), "Pausable: paused");
        }
        _;
    }
    function _totalSupply() internal view returns (uint) {
        return _tokenIdTracker.current();
    }
    function totalMint() public view returns (uint256) {
        return _totalSupply();
    }
    function mint(address _to, uint256 _count) public payable saleIsOpen {
        uint256 total = _totalSupply();
        require(total + _count <= MAX_ELEMENTS, "Max limit");
        require(total <= MAX_ELEMENTS, "Sale end");
        require(_count <= MAX_BY_MINT, "Exceeds number");
        require(msg.value >= price(_count), "Value below price");

        for (uint256 i = 0; i < _count; i++) {
            _mintAnElement(_to);
        }
    }
    function _mintAnElement(address _to) private {
        uint id = _totalSupply();
        _tokenIdTracker.increment();
        _safeMint(_to, id);
        emit CreatePenguin(id);
    }
    function price(uint256 _count) public pure returns (uint256) {
        return PRICE.mul(_count);
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return baseTokenURI;
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        baseTokenURI = baseURI;
    }

    function walletOfOwner(address _owner) external view returns (uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for (uint256 i = 0; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }

        return tokensId;
    }

    function pause(bool val) public onlyOwner {
        if (val == true) {
            _pause();
            return;
        }
        _unpause();
    }

    function withdrawAll() public payable onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0);
        _widthdraw(devAddress, balance.mul(35).div(100));
        _widthdraw(creatorAddress, address(this).balance);
    }

    function _widthdraw(address _address, uint256 _amount) private {
        (bool success, ) = _address.call{value: _amount}("");
        require(success, "Transfer failed.");
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
    
}

minting

function mint(address _to, uint256 _count) public payable saleIsOpen {
    uint256 total = _totalSupply();

    // make sure the requested mint amount does not exceed penguin limit
    require(total + _count <= MAX_ELEMENTS, "Max limit");
    
    // make sure there are still penguins to mint
    require(total <= MAX_ELEMENTS, "Sale end");
    
    // make sure only a limited amount of penguins can be minted
    require(_count <= MAX_BY_MINT, "Exceeds number");
    
    // finally, make sure the user is paying enough
    require(msg.value >= price(_count), "Value below price");

    // for loop is safe because there is a limit to the number of loops (MAX_BY_MINT)
    for (uint256 i = 0; i < _count; i++) {
        _mintAnElement(_to);
    }
}

/*
 * interesting token generation:
   * pull the current value, use it as the id
   * increment value
 * this seems backwards in my simpleton brain but oh well, it still works
*/
function _mintAnElement(address _to) private {
    uint id = _totalSupply();
    _tokenIdTracker.increment();
    _safeMint(_to, id);
    emit CreatePenguin(id);
}

token owner 'wallet'

Finds all the tokens for a given address. This reduces calls into the contract to do the same thing.

function walletOfOwner(address _owner) external view returns (uint256[] memory) {
uint256 tokenCount = balanceOf(_owner);

uint256[] memory tokensId = new uint256[](tokenCount);
for (uint256 i = 0; i < tokenCount; i++) {
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}

return tokensId;
}

To do with javascrpit/ethers (assuming contract implements IERC721Enumerable):

async function walletOfOwner (contract, owner) {
const ownerTotal = await contract.balanceOf(owner)

return Promise.all(
(new Array(ownerTotal)).fill(null)
.map((_, idx) => contract.tokenOfOwnerByIndex(owner, idx))
)
}

https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Enumerable

withdraw ether

Withdrawing ETH gives a cut to the developer and then the rest goes to the 'creator'. Kind of neat.

function withdrawAll() public payable onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0);
_widthdraw(devAddress, balance.mul(35).div(100));
_widthdraw(creatorAddress, address(this).balance);
}

function _widthdraw(address _address, uint256 _amount) private {
// the `call` function is used so that funds can be withdrawn to a smart contract and avoid gas problems
// _I think_
(bool success, ) = _address.call{value: _amount}("");
require(success, "Transfer failed.");
}